package com.dd.seckill.cluster.utils.redisdatainit;

import redis.clients.jedis.*;
import redis.clients.jedis.commands.JedisCommands;

import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.*;
import java.sql.Connection;
import java.util.*;

/**
 * @Author liuxianmeng
 * @CreateTime 2025/9/6 23:28
 * @Description 用户Token初始化 将模拟的用户token在系统启动前存入Redis集群，确保每个分片都有完整数据
 */
public class UserTokenInitializer {
    // 数据库连接配置
    private static final String DB_URL = "jdbc:mysql://localhost:3306/dd_seckill?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai";
    private static final String DB_USERNAME = "root";
    private static final String DB_PASSWORD = "root";

    // Redis集群连接配置 TODO 使用时修改REDIS_HOST和REDIS_PASSWORD
    private static final String REDIS_HOST = "REDIS_HOST";
    private static final int REDIS_PORT = 2821;
    private static final String REDIS_PASSWORD = "REDIS_PASSWORD";

    // Redis键前缀
    public static final String REDIS_TOKEN_KEY_PREFIX = "seckill:user:token:";
    public static final String REDIS_USERID_KEY_PREFIX = "seckill:token:user:";

    // 字符集用于生成随机token
    private static final String CHAR_SET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
    private static final int TOKEN_LENGTH = 16;

    public static void main(String[] args) {
        Connection dbConn = null;
        JedisCommands jedis = null;
        PrintWriter fileWriter = null;

        try {
            // 1. 建立数据库连接
            System.out.println("正在连接数据库...");
            dbConn = DriverManager.getConnection(DB_URL, DB_USERNAME, DB_PASSWORD);

            // 2. 建立Redis连接（尝试多种连接方式）
            System.out.println("正在连接Redis...");
            jedis = createRedisConnection();

            // 3. 创建文件输出流
            System.out.println("创建token文件...");
            fileWriter = new PrintWriter(new FileWriter("user_token.txt", true));

            // 4. 查询所有用户信息
            System.out.println("查询用户数据...");
            String querySql = "SELECT id, username FROM user LIMIT 10000"; // 限制10000条
            PreparedStatement pstmt = dbConn.prepareStatement(querySql);
            ResultSet rs = pstmt.executeQuery();

            // 5. 遍历用户信息并生成token
            System.out.println("开始生成token并写入Redis...");
            int count = 0;
            Random random = new Random();
            List<String> batchTokens = new ArrayList<>();

            while (rs.next()) {
                long userId = rs.getLong("id");
                String username = rs.getString("username");

                // 生成随机16位token
                String token = generateRandomToken(random);

                // 写入Redis - 两种键存储方式方便查询
                String userTokenKey = REDIS_TOKEN_KEY_PREFIX + userId;
                String tokenUserKey = REDIS_USERID_KEY_PREFIX + token;

                // 使用pipeline批量操作提高性能
                if (jedis instanceof Jedis) {
                    jedis.set(userTokenKey, token);
                    jedis.set(tokenUserKey, String.valueOf(userId));
                } else if (jedis instanceof JedisCluster) {
                    jedis.set(userTokenKey, token);
                    jedis.set(tokenUserKey, String.valueOf(userId));
                }

                // 写入文件
                fileWriter.println(userId + "," + username + "," + token);
                batchTokens.add(token);

                count++;
                if (count % 1000 == 0) {
                    System.out.println("已处理 " + count + " 个用户");
                    // 每1000条提交一次文件写入
                    fileWriter.flush();

                    // 短暂休息避免压力过大
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                }

                if (count >= 10000) {
                    break;
                }
            }

            System.out.println("Token初始化完成！共处理 " + count + " 个用户");

            // 6. 验证数据写入
            verifyDataWrite(jedis, count, batchTokens);

        } catch (SQLException e) {
            System.err.println("数据库操作失败: " + e.getMessage());
            e.printStackTrace();
        } catch (IOException e) {
            System.err.println("文件操作失败: " + e.getMessage());
            e.printStackTrace();
        } catch (Exception e) {
            System.err.println("Redis连接失败: " + e.getMessage());
            e.printStackTrace();
            System.out.println("请检查Redis配置：");
            System.out.println("主机: " + REDIS_HOST);
            System.out.println("端口: " + REDIS_PORT);
            System.out.println("密码: " + "已设置");
        } finally {
            // 关闭资源
            try {
                if (dbConn != null) dbConn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }

            if (jedis != null) {
                if (jedis instanceof Jedis) {
                    ((Jedis) jedis).close();
                } else if (jedis instanceof JedisCluster) {
                    ((JedisCluster) jedis).close();
                }
            }

            if (fileWriter != null) fileWriter.close();
        }
    }

    /**
     * 创建Redis连接（支持单机和集群模式）
     */
    public static JedisCommands createRedisConnection() {
        try {
            // 先尝试单机模式连接（腾讯云集群通常支持单机模式访问）
            System.out.println("尝试单机模式连接...");
            Jedis jedis = new Jedis(REDIS_HOST, REDIS_PORT);
            jedis.auth(REDIS_PASSWORD);
            // 测试连接
            jedis.ping();
            System.out.println("单机模式连接成功");
            return jedis;

        } catch (Exception e) {
            System.out.println("单机模式连接失败，尝试集群模式...");

            try {
                // 集群模式连接
                Set<HostAndPort> clusterNodes = new HashSet<>();
                // 腾讯云Redis集群可能需要直接使用提供的地址
                clusterNodes.add(new HostAndPort(REDIS_HOST, REDIS_PORT));

                JedisPoolConfig poolConfig = new JedisPoolConfig();
                poolConfig.setMaxTotal(50);
                poolConfig.setMaxIdle(10);
                poolConfig.setMinIdle(2);

                JedisCluster jedisCluster;
                jedisCluster = new JedisCluster(clusterNodes, "redis", REDIS_PASSWORD);

                // 测试集群连接
                jedisCluster.set("test_connection", "success");
                System.out.println("集群模式连接成功");
                return jedisCluster;

            } catch (Exception ex) {
                System.err.println("集群模式也连接失败: " + ex.getMessage());
                throw new RuntimeException("无法连接到Redis服务器，请检查网络和配置", ex);
            }
        }
    }

    /**
     * 生成随机16位token
     */
    private static String generateRandomToken(Random random) {
        StringBuilder tokenBuilder = new StringBuilder(TOKEN_LENGTH);
        for (int i = 0; i < TOKEN_LENGTH; i++) {
            int index = random.nextInt(CHAR_SET.length());
            tokenBuilder.append(CHAR_SET.charAt(index));
        }
        return tokenBuilder.toString();
    }

    /**
     * 验证数据写入
     */
    private static void verifyDataWrite(JedisCommands jedis, int totalCount, List<String> tokens) {
        try {
            System.out.println("开始验证数据写入...");
            int successCount = 0;
            int sampleSize = Math.min(100, totalCount);

            for (int i = 0; i < sampleSize; i++) {
                String token = tokens.get(i);
                String key = REDIS_USERID_KEY_PREFIX + token;

                String userId = null;
                if (jedis instanceof Jedis) {
                    userId = jedis.get(key);
                } else if (jedis instanceof JedisCluster) {
                    userId = jedis.get(key);
                }

                if (userId != null) {
                    successCount++;
                }
            }

            System.out.println("数据验证完成: " + successCount + "/" + sampleSize + " 个样本验证成功");
            System.out.println("成功率: " + (successCount * 100.0 / sampleSize) + "%");

        } catch (Exception e) {
            System.err.println("数据验证过程中出现错误: " + e.getMessage());
        }
    }

    /**
     * 备用的简单连接方法（如果上述方法都失败）
     */
    private static Jedis createSimpleRedisConnection() {
        Jedis jedis = new Jedis(REDIS_HOST, REDIS_PORT);
        jedis.auth(REDIS_PASSWORD);
        return jedis;
    }
}
